/**
 * 
 */
package com.google.code.netz.wfe.store;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;

import com.google.code.netz.wfe.api.EntityNotFoundException;
import com.google.code.netz.wfe.model.IPetriNetEntity;
import com.google.inject.Inject;
import com.google.inject.Provider;

/**
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @version $Id: JpaStore.java 17 2009-02-09 13:08:49Z rahul.thakur.xdev $
 * @since 1.0
 */
public class JpaStore<T extends IPetriNetEntity, Q extends Query<T>> implements Store<T, Q> {

	/**
	 * {@link Provider} implementation that abstracts away the sourcing of an
	 * {@link EntityManager} instance.
	 */
	@Inject
	private Provider<EntityManager> entityManagerProvider;

	/**
	 * @{inheritDoc
	 * 
	 * @see com.google.code.cisx.store.api.Store#delete(java.lang.Object)
	 */
	@Transactional
	public void delete(T entity) throws StoreException {
		this.entityManagerProvider.get().remove(entity);
	}

	/**
	 * @{inheritDoc
	 * 
	 * @see com.google.code.cisx.store.api.Store#lookup(Class, java.lang.Long)
	 */
	public T lookup(Class<T> klass, Long id) throws StoreException, EntityNotFoundException {
		if (id == null)
			throw new EntityNotFoundException();
		T obj = this.entityManagerProvider.get().find(klass, id);
		if (obj == null)
			throw new EntityNotFoundException(String.format("Unable to locate %s instance for id %s", klass
					.getSigners(), id));
		return obj;
	}

	/**
	 * @{inheritDoc
	 * 
	 * @see com.google.code.cisx.store.api.Store#query(org.apache.maven.continuum.store.api.Query,
	 *      long, long)
	 */
	@SuppressWarnings("unchecked")
	public List<T> query(Q query, int startIdx, int range) throws StoreException {
		Map<String, Object> where = new HashMap<String, Object>();
		String q = query.toString(where);
		List<T> results = find(q, where, startIdx, range);
		return results;
	}

	/**
	 * @{inheritDoc
	 * 
	 * @see com.google.code.cisx.store.api.Store#save(java.lang.Object)
	 */
	@Transactional
	public T save(T entity) throws StoreException {
		EntityManager em = this.entityManagerProvider.get();
		if (null != entity) {
			if (null == entity.getId())
				em.persist(entity);
			else
				entity = em.merge(entity);
		}
		return entity;
	}

	/**
	 * Prepares and executes a query using the 'where' criteria, a start index
	 * and a given range of results to return.
	 * 
	 * @param queryString
	 * @param whereParams
	 * @param startIndex
	 * @param range
	 * @return
	 * @throws DataAccessException
	 */
	protected List find(final String queryString, final Map<String, Object> whereParams, final int startIndex,
			final int range) {
		javax.persistence.Query query = this.entityManagerProvider.get().createQuery(queryString);
		if (whereParams != null) {
			for (Iterator it = whereParams.entrySet().iterator(); it.hasNext();) {
				Map.Entry<String, Object> entry = (Map.Entry<String, Object>) it.next();
				query.setParameter(entry.getKey(), entry.getValue());
			}
		}
		if (startIndex > 0) {
			query.setFirstResult(startIndex);
		}
		if (range > 0) {
			query.setMaxResults(range);
		}
		return query.getResultList();
	}

}
